package com.feng.thread;

/**
 * 需求：建立三个线程，A线程打印10次A，B线程打印10次B,C线程打印10次C，要求线程同时运行，交替打印10次ABC
 * Obj.wait()，与Obj.notify()必须要与synchronized(Obj)一起使用，也就是wait,
 * 与notify是针对已经获取了Obj锁进行操作，
 * 从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。
 * 从功能上来说wait就是说线程在获取对象锁后，主动释放对象锁，同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程，
 * 才能继续获取对象锁，并继续执行
 * 
 * @author Qian
 *
 */

public class MyThreadPrinter2 implements Runnable {

	private String name;
	private Object prev;
	private Object self;

	// 有参构造函数，用于初始化对象时传递参数
	private MyThreadPrinter2(String name, Object prev, Object self) {
		// 三个参数，name-线程的名称；prev-前一个线程对象；self-本线程自身对象
		this.name = name;
		this.prev = prev;
		this.self = self;
	}

	@Override
	public void run() {
		int count = 10;// 局部变量，每个线程都拥有的数据
		boolean flag = true;
		while (flag) {
			if (count > 0) {

				/**
				 * name,pre,self "A", c, a "B", a, b "C", b, c
				 * 每一个线程必须同时持有两个对象锁，才能继续执行
				 * 演示：A，c+a->a.notify()+c.wait();B,a+b->b.notify+a.wait();
				 * C,b+c->c.notify+b.wait; A,c+a->c+a->a.notify+a.wait;......
				 */
				synchronized (prev) {// 得到前一个线程持有的对象锁
					synchronized (self) {// 获得自身对象锁
						System.out.print(name);
						count--;
						// System.err.print(count);
						// 唤醒自身(从第二次循环开始起作用)，释放自身对象锁，用于唤醒下一个线程（即成为了下一线程的前一个线程）
						self.notify();
					}
					try {
						prev.wait();// 前一个线程等待，但释放了对象锁
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

			} else {//循环结束
				synchronized (self) {
					self.notify();//唤醒等待的线程，从而结束进程。不然会B、C线程会一致处于阻塞状态
				}
				flag = false;
			}
		}
	}

	public static void main(String[] args) throws Exception {
		Object a = new Object();
		Object b = new Object();
		Object c = new Object();
		MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
		MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
		MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);

		new Thread(pa).start();
		// 确保按顺序A、B、C执行，若不等待，不能确保按ABC顺序输出，因为三个线程会一起到synchronized处
		Thread.sleep(100);
		new Thread(pb).start();
		Thread.sleep(100);
		new Thread(pc).start();
		Thread.sleep(100);
	}
}